package com.campus.lostandfound.service.impl;

import com.campus.lostandfound.service.VerifyCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 验证码Service实现类（Redis版本）
 * 使用Redis存储验证码，支持自动过期
 * 
 * Redis应用场景：
 * 1. 缓存验证码数据（key-value存储）
 * 2. 自动过期机制（TTL）
 * 3. 防止频繁发送（时间窗口限制）
 */
@Service
public class VerifyCodeServiceImpl implements VerifyCodeService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // Redis key前缀
    private static final String CODE_PREFIX = "verify_code:";      // 验证码key前缀
    private static final String ERROR_COUNT_PREFIX = "verify_error:";  // 错误次数key前缀
    
    // 验证码有效期：5分钟
    private static final long CODE_EXPIRE_TIME = 5;  // 单位：分钟
    
    // 发送间隔：60秒
    private static final long SEND_INTERVAL = 60;  // 单位：秒
    
    // 验证码长度
    private static final int CODE_LENGTH = 6;
    
    // 最大错误次数
    private static final int MAX_ERROR_COUNT = 5;
    
    @Override
    public String sendVerifyCode(String phone) {
        String codeKey = CODE_PREFIX + phone;
        
        // 检查Redis中是否已存在未过期的验证码（防止频繁发送）
        CodeInfo existingCode = (CodeInfo) redisTemplate.opsForValue().get(codeKey);
        if (existingCode != null) {
            long timeSinceLastSend = (System.currentTimeMillis() - existingCode.getSendTime()) / 1000;
            if (timeSinceLastSend < SEND_INTERVAL) {
                long remainingSeconds = SEND_INTERVAL - timeSinceLastSend;
                throw new RuntimeException("验证码发送过于频繁，请" + remainingSeconds + "秒后再试");
            }
        }
        
        // 生成6位随机数字验证码
        String code = generateCode();
        
        // 将验证码存储到Redis，设置5分钟过期
        CodeInfo codeInfo = new CodeInfo(code, System.currentTimeMillis());
        redisTemplate.opsForValue().set(codeKey, codeInfo, CODE_EXPIRE_TIME, TimeUnit.MINUTES);
        
        // 初始化错误计数器（也设置5分钟过期）
        String errorKey = ERROR_COUNT_PREFIX + phone;
        redisTemplate.opsForValue().set(errorKey, 0, CODE_EXPIRE_TIME, TimeUnit.MINUTES);
        
        // 模拟发送短信（实际项目应该调用短信服务）
        System.out.println("===========================================");
        System.out.println("【Redis验证码】手机号：" + phone);
        System.out.println("【Redis验证码】验证码：" + code);
        System.out.println("【Redis验证码】有效期：5分钟");
        System.out.println("【Redis验证码】存储位置：Redis");
        System.out.println("===========================================");
        
        return code; // 实际项目中不应返回验证码
    }
    
    @Override
    public boolean verifyCode(String phone, String code) {
        String codeKey = CODE_PREFIX + phone;
        String errorKey = ERROR_COUNT_PREFIX + phone;
        
        // 从Redis获取验证码
        CodeInfo codeInfo = (CodeInfo) redisTemplate.opsForValue().get(codeKey);
        
        if (codeInfo == null) {
            throw new RuntimeException("验证码不存在或已过期");
        }
        
        // 检查错误次数
        Integer errorCount = (Integer) redisTemplate.opsForValue().get(errorKey);
        if (errorCount != null && errorCount >= MAX_ERROR_COUNT) {
            // 错误次数过多，删除验证码
            redisTemplate.delete(codeKey);
            redisTemplate.delete(errorKey);
            throw new RuntimeException("验证码错误次数过多，请重新获取");
        }
        
        // 验证验证码
        if (!code.equals(codeInfo.getCode())) {
            // 增加错误计数
            redisTemplate.opsForValue().increment(errorKey);
            errorCount = errorCount == null ? 1 : errorCount + 1;
            throw new RuntimeException("验证码错误，还可以尝试" + (MAX_ERROR_COUNT - errorCount) + "次");
        }
        
        return true;
    }
    
    @Override
    public void clearCode(String phone) {
        String codeKey = CODE_PREFIX + phone;
        String errorKey = ERROR_COUNT_PREFIX + phone;
        
        // 从Redis删除验证码和错误计数
        redisTemplate.delete(codeKey);
        redisTemplate.delete(errorKey);
        
        System.out.println("【Redis】已清除手机号 " + phone + " 的验证码");
    }
    
    /**
     * 生成随机验证码
     */
    private String generateCode() {
        Random random = new Random();
        StringBuilder code = new StringBuilder();
        for (int i = 0; i < CODE_LENGTH; i++) {
            code.append(random.nextInt(10));
        }
        return code.toString();
    }
    
    /**
     * 验证码信息内部类（需要实现Serializable以支持Redis序列化）
     * 注意：由于使用Jackson JSON序列化，需要提供无参构造函数和setter方法
     */
    private static class CodeInfo implements Serializable {
        private static final long serialVersionUID = 1L;
        
        private String code;
        private long sendTime;
        
        // 无参构造函数（Jackson反序列化需要）
        public CodeInfo() {
        }
        
        // 有参构造函数
        public CodeInfo(String code, long sendTime) {
            this.code = code;
            this.sendTime = sendTime;
        }
        
        public String getCode() {
            return code;
        }
        
        public void setCode(String code) {
            this.code = code;
        }
        
        public long getSendTime() {
            return sendTime;
        }
        
        public void setSendTime(long sendTime) {
            this.sendTime = sendTime;
        }
    }
}
